// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// 首页查询结构体定义
package homepage

import (
	"fleetmanager/api/common/query"
	"fleetmanager/api/errors"
	"fleetmanager/api/model/fleet"
	"fleetmanager/api/model/homepage"
	"fleetmanager/api/params"
	"fleetmanager/api/service/base"
	"fleetmanager/api/service/constants"
	"fleetmanager/db/dao"
	"fleetmanager/logger"
	"fmt"
	"sort"
	"strconv"
	"sync"

	"github.com/beego/beego/v2/server/web/context"
)

// TODO: 首页若需增加信息，只需在该部分增加对应部分的Service类
type HomepageService struct {
	FleetService base.FleetService
}

// NewHomepageService 新建homepage服务
func NewHomepageService(ctx *context.Context, Logger *logger.FMLogger) *HomepageService {
	return &HomepageService{
		FleetService: base.FleetService{
			Ctx:    ctx,
			Logger: Logger,
		},
	}
}

func (s *HomepageService) ShowHomepageInfo() (*homepage.FleetRuntimeState, *errors.CodedError) {
	s.FleetService.Logger.Info("receive one request to query homepage")
	queryFilter := make(map[string]interface{})
	queryFilter["state"] = dao.FleetStateActive
	queryFilter["project_id"] = s.FleetService.Ctx.Input.Param(params.ProjectId)

	allFleets, err := dao.GetFleetStorage().ListOrder(queryFilter, s.GetSortField(),
		query.DefaultOffset, query.MaxiLimit)
	if err != nil {
		s.FleetService.Logger.Error("query homapage list fleets error: %+v", err)
		return nil, errors.NewError(errors.DBError)
	}
	homepageFleets := &homepage.FleetRuntimeState{
		TotalCount: int32(len(allFleets)),
		Fleets:     []homepage.Fleet{},
	}
	wg := sync.WaitGroup{}
	wg.Add(len(allFleets))
	for _, ft := range allFleets {
		go func(dft dao.Fleet) {
			defer wg.Done()
			fleetInfo := &homepage.FleetInfo{
				FleetId:      dft.Id,
				FleetName:    dft.Name,
				State:        dft.State,
				Region:       dft.Region,
				CreationTime: dft.CreationTime.Format(constants.TimeFormatLayout),
			}
			homepageFleet, errC := s.QueryFleetRuntime(*fleetInfo)
			if errC != nil {
				s.FleetService.Logger.Error("query homepage fleet %s error %+v, step it", fleetInfo.FleetId, errC)
			} else {
				homepageFleets.Fleets = append(homepageFleets.Fleets, *homepageFleet)
			}
		}(ft)
	}
	wg.Wait()
	// 按照 order_by 排序
	s.SortFleetRuntime(s.FleetService.Ctx.Input.Query(constants.FleetOrderBy), homepageFleets)
	// 取top
	if errC := s.GetTopFleet(homepageFleets); errC != nil {
		return nil, errC
	}
	homepageFleets.Count = int32(len(homepageFleets.Fleets))
	return homepageFleets, nil
}

// 按照时间排序获取fleet的首页信息，支持通过实例数/进程数/会话数/时间排序
// TODO: 后续可以增加排序方式
func (s *HomepageService) SortFleetRuntime(mode string, hFleets *homepage.FleetRuntimeState) {
	switch mode {
	case constants.HomepageSortByInstanceAsc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].InstanceCount < hFleets.Fleets[j].InstanceCount})
		return
	case constants.HomepageSortByInstanceDesc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].InstanceCount > hFleets.Fleets[j].InstanceCount})
		return
	}
	switch mode {
	case constants.HomepageSortByProcessAsc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].AppProcessCount < hFleets.Fleets[j].AppProcessCount})
		return
	case constants.HomepageSortByProcessDesc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].AppProcessCount > hFleets.Fleets[j].AppProcessCount})
		return
	}
	switch mode {
	case constants.HomepageSortByServerSessionAsc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].ServerSessionCount < hFleets.Fleets[j].ServerSessionCount})
		return
	case constants.HomepageSortByServerSessionDesc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].ServerSessionCount > hFleets.Fleets[j].ServerSessionCount})
		return
	}
	switch mode {
	case constants.HomepageSortByCreationTimeAsc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].CreationTime < hFleets.Fleets[j].CreationTime})
		return
	case constants.HomepageSortByCreationTimeDesc:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].CreationTime > hFleets.Fleets[j].CreationTime})
		return
	// 默认按时间降序排序
	default:
		sort.Slice(hFleets.Fleets, func(i, j int) bool {return hFleets.Fleets[i].CreationTime > hFleets.Fleets[j].CreationTime})
		return
	}
}

func (s *HomepageService) GetTopFleet(fleets *homepage.FleetRuntimeState) *errors.CodedError {
	paramTop := s.FleetService.Ctx.Input.Query(constants.FleetTopCount)
	if paramTop == "" {
		return nil
	}
	top, err := strconv.Atoi(paramTop)
	if err != nil {
		s.FleetService.Logger.Error("query homepage top param invalide: %s, err: %+v", paramTop, err)
		return errors.NewError(errors.InvalidParameterValue)
	}
	if len(fleets.Fleets) <= top {
		return nil
	}
	fleets.Fleets = fleets.Fleets[:top]
	return nil
}

func (s *HomepageService) GetSortField() string {
	sortMode := s.FleetService.Ctx.Input.Query(constants.FleetOrderBy)
	switch sortMode {
	case constants.HomepageSortByCreationTimeAsc:
		return constants.FleetCreationTimeAsc
	case constants.HomepageSortByCreationTimeDesc:
		return constants.FleetCreationTimeDesc
	default:
		return constants.FleetCreationTimeDesc
	}
}

func (s *HomepageService) BuildFleetRuntimeState(fleetInfo homepage.FleetInfo, instanceStateCounts []homepage.Instance,
	processStateCounts []homepage.AppProcess, serverSessionCounts []homepage.ServerSession) (*homepage.Fleet, *errors.CodedError) {
	fleetRuntime := &homepage.Fleet{
		FleetId:        fleetInfo.FleetId,
		FleetName:      fleetInfo.FleetName,
		State:          fleetInfo.State,
		CreationTime:   fleetInfo.CreationTime,
		Instances:      instanceStateCounts,
		AppProcesses:   processStateCounts,
		ServerSessions: serverSessionCounts,
	}
	for _, ins := range instanceStateCounts {
		if ins.State == constants.InstanceHealthStateNormal {
			fleetRuntime.InstanceCount = ins.Count
			break
		}
	}
	for _, app := range processStateCounts {
		if app.State == constants.AppProcessStateActive {
			fleetRuntime.AppProcessCount = app.Count
			break
		}
	}
	for _, ss := range serverSessionCounts {
		if ss.State == constants.ServerSessionStateActive {
			fleetRuntime.ServerSessionCount = ss.Count
			break
		}
	}

	return fleetRuntime, nil
}

func (s *HomepageService) GenerateInstanceState(state string, insResp *fleet.ListInstanceResonseFromAASS) *homepage.Instance {
	return &homepage.Instance{
		State: state,
		Count: int32(insResp.TotalNumber),
	}
}

func (s *HomepageService) ShowInstancesByFleetId(fleetId string) ([]homepage.Instance, *errors.CodedError) {
	scalingGroup, err := dao.GetScalingGroupStorage().GetOne(dao.Filters{"FleetId": fleetId})
	if err != nil {
		s.FleetService.Logger.Error("Get scaling group by fleet id %s error: %+v", fleetId, err)
		return nil, errors.NewError(errors.DBError)
	}
	instancesState := []homepage.Instance{}
	wg := sync.WaitGroup{}
	instancHealthStates := []string{
		constants.InstanceHealthStateNormal,
		constants.InstanceHealthStateInitializing,
		constants.InstanceHealthStateError,
	}
	wg.Add(len(instancHealthStates))
	for _, healthState := range instancHealthStates {
		go func(hs string) {
			defer wg.Done()
			queryParam := map[string]string{
				params.QueryFleetId: fleetId,
				params.HealthState:  hs,
			}
			code, rsp, err := base.ForwardToAASS(s.FleetService.Ctx, scalingGroup.RegionId,
				fmt.Sprintf(constants.AASSMonitorInstancesUrlPattern,
					scalingGroup.ProjectId), queryParam)
			respAass := &fleet.ListInstanceResonseFromAASS{
				Instances: []fleet.InstanceResponseFromAASS{},
			}
			if NewErrC := base.AcceptResp(s.FleetService.Logger, respAass, code, rsp, err); NewErrC != nil {
				s.FleetService.Logger.Error("Get instance info from aass error: %+v, query params: %+v", NewErrC, queryParam)
			} else if respAass.TotalNumber == 0 {
				s.FleetService.Logger.Info("health state %s instance count %d", hs, respAass.TotalNumber)
			} else {
				instancesState = append(instancesState, *s.GenerateInstanceState(hs, respAass))
			}
		}(healthState)
	}
	wg.Wait()
	return instancesState, nil
}

func (s *HomepageService) ShowAppProcessByFleetId(fleetId string, region string) (
	[]homepage.AppProcess, *errors.CodedError) {
	queryParam := map[string]string{
		params.QueryFleetId: fleetId,
	}
	code, rsp, NewErr := base.ForwardToAppgw(s.FleetService.Ctx, region,
		constants.ProcessCountsUrl, queryParam)
	respAppg := &homepage.ShowAppProcessStatesResponse{
		FleetID:       fleetId,
		ProcessCounts: []homepage.AppProcess{},
	}
	if NewErrC := base.AcceptResp(s.FleetService.Logger, respAppg, code, rsp, NewErr); NewErrC != nil {
		return nil, NewErrC
	}

	return respAppg.ProcessCounts, nil
}

func (s *HomepageService) ShowServerSessionByFleetId(fleetId string, region string) (
	[]homepage.ServerSession, *errors.CodedError) {
	queryParam := map[string]string{
		params.QueryFleetId: fleetId,
	}
	code, rsp, NewErr := base.ForwardToAppgw(s.FleetService.Ctx, region,
		constants.ServerSessionCountsUrl, queryParam)
	respAppg := &homepage.ShowServerSessionStatesResponse{
		FleetID:             fleetId,
		ServerSessionCounts: []homepage.ServerSession{},
	}
	if NewErrC := base.AcceptResp(s.FleetService.Logger, respAppg, code, rsp, NewErr); NewErrC != nil {
		return nil, NewErrC
	}

	return respAppg.ServerSessionCounts, nil
}

func (s *HomepageService) QueryFleetRuntime(fleetInfo homepage.FleetInfo) (*homepage.Fleet, *errors.CodedError) {
	instanceStates, err := s.ShowInstancesByFleetId(fleetInfo.FleetId)
	if err != nil {
		return nil, err
	}
	appProcessStates, err := s.ShowAppProcessByFleetId(fleetInfo.FleetId, fleetInfo.Region)
	if err != nil {
		return nil, err
	}
	serverSessionStates, err := s.ShowServerSessionByFleetId(fleetInfo.FleetId, fleetInfo.Region)
	if err != nil {
		return nil, err
	}
	fleetRuntime, err := s.BuildFleetRuntimeState(fleetInfo, instanceStates, appProcessStates, serverSessionStates)
	if err != nil {
		return nil, err
	}
	return fleetRuntime, nil
}
